"""
第3章：响应模型与状态码 - 实战练习代码
配合 chapter03_response.md 讲义使用

运行方式：
cd /Users/baimu/PycharmProjects/fastApiProject3
uvicorn stage1_basics.chapter03_response_practice:app --reload --port 8004

访问：
- Swagger UI: http://127.0.0.1:8004/docs
- 根路径: http://127.0.0.1:8004/
"""

from fastapi import FastAPI, HTTPException, status, Response
from fastapi.responses import JSONResponse, HTMLResponse, PlainTextResponse
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, List, Dict
from datetime import datetime

# ========== 应用初始化 ==========

app = FastAPI(
    title="第3章：响应模型与状态码练习",
    description="FastAPI 响应处理的完整示例",
    version="1.0.0",
)

# ========== 模拟数据库 ==========

users_db: Dict[int, dict] = {}
posts_db: Dict[int, dict] = {}
next_user_id = 1
next_post_id = 1

# ========== 1. 响应模型基础 ==========


class UserBase(BaseModel):
    """用户基础模型"""

    username: str
    email: EmailStr


class UserCreate(UserBase):
    """创建用户输入模型"""

    password: str = Field(..., min_length=8)


class UserResponse(UserBase):
    """用户响应模型（不包含密码）"""

    id: int
    created_at: datetime

    class Config:
        json_schema_extra = {
            "example": {
                "id": 1,
                "username": "john_doe",
                "email": "john@example.com",
                "created_at": "2024-01-01T00:00:00",
            }
        }


@app.post(
    "/demo/users",
    response_model=UserResponse,
    status_code=status.HTTP_201_CREATED,
    tags=["响应模型基础"],
)
async def create_user_demo(user: UserCreate):
    """
    响应模型基础示例
    
    演示：
    - response_model 自动过滤敏感字段（password）
    - 返回 201 状态码
    """
    global next_user_id

    user_data = {
        "id": next_user_id,
        "username": user.username,
        "email": user.email,
        "password": f"hashed_{user.password}",  # ← 会被自动过滤
        "created_at": datetime.now(),
    }

    next_user_id += 1

    return user_data


# ========== 2. HTTP 状态码 ==========


@app.get("/demo/status-codes/200", tags=["HTTP状态码"])
async def status_200():
    """200 OK - 成功（默认）"""
    return {"status": 200, "message": "请求成功"}


@app.post(
    "/demo/status-codes/201",
    status_code=status.HTTP_201_CREATED,
    tags=["HTTP状态码"],
)
async def status_201():
    """201 Created - 资源创建成功"""
    return {"status": 201, "message": "资源创建成功"}


@app.delete(
    "/demo/status-codes/204",
    status_code=status.HTTP_204_NO_CONTENT,
    tags=["HTTP状态码"],
)
async def status_204():
    """204 No Content - 删除成功，无返回内容"""
    return None


@app.get("/demo/status-codes/404", tags=["HTTP状态码"])
async def status_404():
    """404 Not Found - 资源不存在"""
    raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="资源不存在")


@app.post("/demo/status-codes/409", tags=["HTTP状态码"])
async def status_409():
    """409 Conflict - 资源冲突"""
    raise HTTPException(
        status_code=status.HTTP_409_CONFLICT, detail="资源已存在，无法创建"
    )


# ========== 3. HTTPException 错误处理 ==========


@app.get("/demo/errors/simple", tags=["错误处理"])
async def simple_error():
    """
    简单错误示例
    
    返回基本的错误信息
    """
    raise HTTPException(status_code=404, detail="用户不存在")


@app.get("/demo/errors/detailed", tags=["错误处理"])
async def detailed_error():
    """
    详细错误示例
    
    返回结构化的错误信息
    """
    raise HTTPException(
        status_code=400,
        detail={
            "error": "validation_error",
            "message": "邮箱格式不正确",
            "field": "email",
            "received": "invalid-email",
        },
    )


@app.get("/demo/errors/with-headers", tags=["错误处理"])
async def error_with_headers():
    """
    带自定义响应头的错误
    """
    raise HTTPException(
        status_code=404,
        detail="资源不存在",
        headers={"X-Error-Code": "RESOURCE_NOT_FOUND", "X-Request-ID": "12345"},
    )


# ========== 4. 响应模型选项 ==========


class FullUser(BaseModel):
    """完整用户模型"""

    id: int
    username: str
    email: EmailStr
    password: str
    is_active: bool
    role: str


@app.get(
    "/demo/response-options/exclude",
    response_model=FullUser,
    response_model_exclude={"password"},
    tags=["响应模型选项"],
)
async def exclude_field():
    """
    response_model_exclude 示例
    
    排除敏感字段（password）
    """
    return {
        "id": 1,
        "username": "john",
        "email": "john@example.com",
        "password": "secret123",  # ← 会被排除
        "is_active": True,
        "role": "user",
    }


@app.get(
    "/demo/response-options/include",
    response_model=FullUser,
    response_model_include={"id", "username", "email"},
    tags=["响应模型选项"],
)
async def include_field():
    """
    response_model_include 示例
    
    只包含指定字段
    """
    return {
        "id": 1,
        "username": "john",
        "email": "john@example.com",
        "password": "secret123",
        "is_active": True,
        "role": "user",
    }


class OptionalUser(BaseModel):
    """带可选字段的用户模型"""

    id: int
    username: str
    email: EmailStr
    bio: Optional[str] = None
    website: Optional[str] = None


@app.get(
    "/demo/response-options/exclude-unset",
    response_model=OptionalUser,
    response_model_exclude_unset=True,
    tags=["响应模型选项"],
)
async def exclude_unset():
    """
    response_model_exclude_unset 示例
    
    不返回未设置的字段
    """
    return {"id": 1, "username": "john", "email": "john@example.com"}
    # bio 和 website 未设置，不会出现在响应中


@app.get(
    "/demo/response-options/exclude-none",
    response_model=OptionalUser,
    response_model_exclude_none=True,
    tags=["响应模型选项"],
)
async def exclude_none():
    """
    response_model_exclude_none 示例
    
    不返回值为 None 的字段
    """
    return {
        "id": 1,
        "username": "john",
        "email": "john@example.com",
        "bio": None,  # ← 不会出现在响应中
        "website": None,  # ← 不会出现在响应中
    }


# ========== 5. 自定义响应 ==========


@app.get("/demo/custom/json", tags=["自定义响应"])
async def custom_json():
    """
    自定义 JSONResponse
    """
    content = {"message": "自定义JSON响应", "timestamp": datetime.now().isoformat()}

    return JSONResponse(
        content=content, status_code=200, headers={"X-Custom-Header": "CustomValue"}
    )


@app.get("/demo/custom/html", response_class=HTMLResponse, tags=["自定义响应"])
async def custom_html():
    """
    返回 HTML 响应
    """
    html_content = """
    <!DOCTYPE html>
    <html>
    <head>
        <title>FastAPI HTML Response</title>
        <style>
            body { font-family: Arial, sans-serif; padding: 20px; }
            h1 { color: #009688; }
        </style>
    </head>
    <body>
        <h1>欢迎来到 FastAPI！</h1>
        <p>这是一个 HTML 响应示例</p>
        <ul>
            <li>状态码：200</li>
            <li>Content-Type: text/html</li>
        </ul>
    </body>
    </html>
    """
    return html_content


@app.get("/demo/custom/text", response_class=PlainTextResponse, tags=["自定义响应"])
async def custom_text():
    """
    返回纯文本响应
    """
    return "这是一个纯文本响应\n第二行\n第三行"


# ========== 6. 实战示例：用户管理系统 ==========


class UserInDB(BaseModel):
    """数据库中的用户模型"""

    id: int
    username: str
    email: EmailStr
    hashed_password: str
    full_name: Optional[str] = None
    is_active: bool = True
    created_at: datetime
    updated_at: datetime


class UserUpdate(BaseModel):
    """用户更新模型"""

    email: Optional[EmailStr] = None
    full_name: Optional[str] = None


# 创建用户
@app.post(
    "/api/users",
    response_model=UserResponse,
    status_code=status.HTTP_201_CREATED,
    tags=["用户管理系统"],
    responses={
        201: {"description": "用户创建成功"},
        409: {"description": "用户名或邮箱已存在"},
    },
)
async def create_user(user: UserCreate):
    """
    创建新用户
    
    检查用户名和邮箱是否已存在
    """
    global next_user_id

    # 检查用户名是否存在
    for existing_user in users_db.values():
        if existing_user["username"] == user.username:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"用户名 '{user.username}' 已被使用",
            )

        if existing_user["email"] == user.email:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"邮箱 '{user.email}' 已被注册",
            )

    # 创建用户
    now = datetime.now()
    user_data = {
        "id": next_user_id,
        "username": user.username,
        "email": user.email,
        "hashed_password": f"hashed_{user.password}",
        "full_name": None,
        "is_active": True,
        "created_at": now,
        "updated_at": now,
    }

    users_db[next_user_id] = user_data
    next_user_id += 1

    return user_data


# 获取用户列表
@app.get(
    "/api/users",
    response_model=List[UserResponse],
    tags=["用户管理系统"],
)
async def list_users(skip: int = 0, limit: int = 10):
    """
    获取用户列表
    
    支持分页
    """
    users = list(users_db.values())[skip : skip + limit]
    return users


# 获取单个用户
@app.get(
    "/api/users/{user_id}",
    response_model=UserResponse,
    tags=["用户管理系统"],
    responses={
        200: {"description": "成功返回用户信息"},
        404: {"description": "用户不存在"},
    },
)
async def get_user(user_id: int):
    """
    获取指定用户
    
    如果用户不存在，返回 404
    """
    if user_id not in users_db:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail=f"用户 ID {user_id} 不存在"
        )

    return users_db[user_id]


# 更新用户
@app.patch(
    "/api/users/{user_id}",
    response_model=UserResponse,
    tags=["用户管理系统"],
    responses={
        200: {"description": "用户更新成功"},
        404: {"description": "用户不存在"},
        409: {"description": "邮箱已被其他用户使用"},
    },
)
async def update_user(user_id: int, user_update: UserUpdate):
    """
    更新用户信息
    
    支持部分更新
    """
    if user_id not in users_db:
        raise HTTPException(status_code=404, detail="用户不存在")

    # 检查邮箱是否被其他用户使用
    if user_update.email:
        for uid, u in users_db.items():
            if uid != user_id and u["email"] == user_update.email:
                raise HTTPException(
                    status_code=409, detail=f"邮箱 '{user_update.email}' 已被其他用户使用"
                )

    # 更新用户
    user = users_db[user_id]
    update_data = user_update.dict(exclude_unset=True)

    for field, value in update_data.items():
        user[field] = value

    user["updated_at"] = datetime.now()

    return user


# 删除用户
@app.delete(
    "/api/users/{user_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    tags=["用户管理系统"],
    responses={
        204: {"description": "用户删除成功"},
        404: {"description": "用户不存在"},
    },
)
async def delete_user(user_id: int):
    """
    删除用户
    
    返回 204 No Content
    """
    if user_id not in users_db:
        raise HTTPException(status_code=404, detail="用户不存在")

    del users_db[user_id]
    return None


# ========== 7. 实战示例：博客文章系统 ==========


class PostBase(BaseModel):
    """文章基础模型"""

    title: str = Field(..., min_length=1, max_length=200)
    content: str = Field(..., min_length=10)


class PostCreate(PostBase):
    """创建文章输入模型"""

    pass


class PostUpdate(BaseModel):
    """更新文章模型"""

    title: Optional[str] = Field(None, min_length=1, max_length=200)
    content: Optional[str] = Field(None, min_length=10)
    is_published: Optional[bool] = None


class PostResponse(PostBase):
    """文章响应模型"""

    id: int
    author_id: int
    is_published: bool
    created_at: datetime
    updated_at: datetime

    class Config:
        json_schema_extra = {
            "example": {
                "id": 1,
                "title": "FastAPI 入门教程",
                "content": "这是一篇详细的教程...",
                "author_id": 1,
                "is_published": True,
                "created_at": "2024-01-01T00:00:00",
                "updated_at": "2024-01-01T00:00:00",
            }
        }


# 创建文章
@app.post(
    "/api/posts",
    response_model=PostResponse,
    status_code=status.HTTP_201_CREATED,
    tags=["博客文章系统"],
)
async def create_post(post: PostCreate, author_id: int = 1):
    """
    创建新文章
    
    默认为草稿状态（未发布）
    """
    global next_post_id

    now = datetime.now()
    post_data = {
        "id": next_post_id,
        "title": post.title,
        "content": post.content,
        "author_id": author_id,
        "is_published": False,
        "created_at": now,
        "updated_at": now,
    }

    posts_db[next_post_id] = post_data
    next_post_id += 1

    return post_data


# 获取文章列表
@app.get(
    "/api/posts",
    response_model=List[PostResponse],
    tags=["博客文章系统"],
)
async def list_posts(published_only: bool = False, skip: int = 0, limit: int = 10):
    """
    获取文章列表
    
    - published_only: 只返回已发布的文章
    """
    posts = list(posts_db.values())

    if published_only:
        posts = [p for p in posts if p["is_published"]]

    return posts[skip : skip + limit]


# 获取单篇文章
@app.get(
    "/api/posts/{post_id}",
    response_model=PostResponse,
    tags=["博客文章系统"],
)
async def get_post(post_id: int):
    """获取指定文章"""
    if post_id not in posts_db:
        raise HTTPException(status_code=404, detail=f"文章 ID {post_id} 不存在")

    return posts_db[post_id]


# 更新文章
@app.patch(
    "/api/posts/{post_id}",
    response_model=PostResponse,
    tags=["博客文章系统"],
)
async def update_post(post_id: int, post_update: PostUpdate):
    """
    更新文章
    
    支持部分更新
    """
    if post_id not in posts_db:
        raise HTTPException(status_code=404, detail="文章不存在")

    post = posts_db[post_id]
    update_data = post_update.dict(exclude_unset=True)

    for field, value in update_data.items():
        post[field] = value

    post["updated_at"] = datetime.now()

    return post


# 发布文章
@app.post(
    "/api/posts/{post_id}/publish",
    response_model=PostResponse,
    tags=["博客文章系统"],
)
async def publish_post(post_id: int):
    """
    发布文章
    
    将文章状态改为已发布
    """
    if post_id not in posts_db:
        raise HTTPException(status_code=404, detail="文章不存在")

    post = posts_db[post_id]

    if post["is_published"]:
        raise HTTPException(status_code=400, detail="文章已经发布过了")

    post["is_published"] = True
    post["updated_at"] = datetime.now()

    return post


# 删除文章
@app.delete(
    "/api/posts/{post_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    tags=["博客文章系统"],
)
async def delete_post(post_id: int):
    """删除文章"""
    if post_id not in posts_db:
        raise HTTPException(status_code=404, detail="文章不存在")

    del posts_db[post_id]
    return None


# ========== 8. 统计信息 ==========


@app.get("/api/stats", tags=["统计"])
async def get_stats():
    """
    获取系统统计信息
    """
    active_users = sum(1 for u in users_db.values() if u["is_active"])
    published_posts = sum(1 for p in posts_db.values() if p["is_published"])

    return {
        "users": {
            "total": len(users_db),
            "active": active_users,
            "inactive": len(users_db) - active_users,
        },
        "posts": {
            "total": len(posts_db),
            "published": published_posts,
            "drafts": len(posts_db) - published_posts,
        },
    }


# ========== 根路径 ==========


@app.get("/", tags=["根路径"])
async def root():
    """
    根路径
    
    返回欢迎信息和可用端点
    """
    return {
        "message": "欢迎来到第3章：响应模型与状态码练习！",
        "features": [
            "响应模型（response_model）",
            "HTTP 状态码",
            "错误处理（HTTPException）",
            "响应模型选项",
            "自定义响应",
        ],
        "examples": {
            "用户管理系统": "/api/users",
            "博客文章系统": "/api/posts",
            "统计信息": "/api/stats",
        },
        "docs": "/docs",
    }


# ========== 启动信息 ==========

if __name__ == "__main__":
    print("=" * 70)
    print("📚 第3章：响应模型与状态码 - 实战练习")
    print("=" * 70)
    print("\n启动命令:")
    print(
        "uvicorn stage1_basics.chapter03_response_practice:app --reload --port 8004\n"
    )
    print("访问地址:")
    print("- Swagger UI: http://127.0.0.1:8004/docs")
    print("- ReDoc: http://127.0.0.1:8004/redoc")
    print("- 根路径: http://127.0.0.1:8004/")
    print("\n学习重点:")
    print("1. 理解响应模型的作用（自动过滤敏感字段）")
    print("2. 掌握常用 HTTP 状态码的使用场景")
    print("3. 学会使用 HTTPException 处理错误")
    print("4. 了解响应模型的各种选项")
    print("5. 实战：完整的 CRUD API")
    print("\n" + "=" * 70)

